import re
from typing import Union, Optional
from urllib.parse import urlparse
import logging

from eAuth.constant import CACHE_PREFIX_USER_TO_ROLE, CACHE_TIME_USER, NULL_ID, CACHE_PREFIX_LOGIN_INCORRECT
from eAuth.extensions import cache, db

logger = logging.getLogger(__name__)


class UserCache:
    @staticmethod
    def set_cache(user_id: Union[int, str], role_ids: set):
        cache.set(f"{CACHE_PREFIX_USER_TO_ROLE}_{user_id}", role_ids, CACHE_TIME_USER)

    @staticmethod
    def get_cache(user_id: Union[int, str]):
        return cache.get(f"{CACHE_PREFIX_USER_TO_ROLE}_{user_id}")

    @staticmethod
    def remove_cache(user_id: Union[int, str]):
        cache.delete(f"{CACHE_PREFIX_USER_TO_ROLE}_{user_id}")

    @staticmethod
    def remove_login_incorrect_cache(user):
        try:
            cache.delete(f"{CACHE_PREFIX_LOGIN_INCORRECT}_{user.id}")
            user.login_incorrect = 0
            db.session.commit()
        except:
            db.session.rollback()
            logger.warning(f"[login] Set the login_incorrect to zero failed for user {user.username}", exc_info=True)


class ApiTrie:
    def __init__(self):
        self.children: dict[str, ApiTrie] = {}
        self.is_end = False
        self.methods: Optional[dict[str, int]] = None

    def __repr__(self):
        return f"{self.is_end} - {self.methods} - {self.children}"

    def insert(self, id_: int, method: str, url: str) -> None:
        node = self
        for segment in url.strip("/ ").split("/"):
            # 将{xx}替换为正则
            segment = re.sub(r'{[^}]*?}', r'[a-zA-Z0-9\\u4e00-\\u9fff\_\-\.~]+', segment)
            if segment not in node.children:
                node.children[segment] = ApiTrie()
            node = node.children[segment]
        node.is_end = True
        if node.methods is None:
            node.methods = dict()
        node.methods[method.upper()] = id_

    def search(self, method: str, request_url: str) -> int:
        """
        查询用户请求url是否匹配

        :param method:
        :param request_url:
        :return:
        """
        node = self
        # 请求url标准化
        parsed_url = urlparse(request_url)
        request_url = parsed_url.path
        method = method.upper()

        for segment in request_url.strip("/ ").split("/"):
            # 判断是否存在
            match_segment: Optional[str] = None
            for api_segment, api_node in node.children.items():
                if re.match(f"^{api_segment}$", segment):
                    if match_segment is None or (api_segment == segment):
                        match_segment = api_segment
            if match_segment is None:
                return NULL_ID
            node = node.children[match_segment]
        if node.is_end is False:
            return NULL_ID
        return node.methods.get(method) if method in node.methods else NULL_ID
